import os
import yaml
import json
import uuid
import shutil
import logging
from fastapi import UploadFile
from typing import List, Dict, Any, Union
from app.core.config import settings
from datetime import datetime

class FileManager:
    """文件管理工具类"""
    
    @staticmethod
    async def create_product_folder(product_name: str) -> str:
        """
        创建产品文件夹
        
        Args:
            product_name: 产品名称
        
        Returns:
            产品文件夹路径
        """
        # 使用产品名称加唯一ID创建文件夹，避免重名
        folder_name = f"{product_name}_{uuid.uuid4().hex[:8]}"
        product_path = os.path.join(settings.PRODUCTS_DIR, folder_name)
        os.makedirs(product_path, exist_ok=True)
        
        return product_path
    
    @staticmethod
    async def save_uploaded_file(file: UploadFile, dest_folder: str) -> str:
        """
        保存上传的文件
        
        Args:
            file: 上传的文件
            dest_folder: 目标文件夹
        
        Returns:
            保存后的文件路径
        """
        try:
            # 检查文件是否存在
            if not file:
                logging.error("上传的文件为None")
                raise ValueError("上传的文件无效")
            
            # 检查文件名是否存在
            if not file.filename:
                logging.error("文件名为空")
                filename = f"file_{uuid.uuid4().hex[:8]}"
                file_ext = ".jpg"  # 默认扩展名
            else:
                filename = file.filename
                _, file_ext = os.path.splitext(filename)
            
            logging.info(f"准备保存文件: {filename}, 大小: {getattr(file, 'size', '未知')}")
            
            # 确保目标文件夹存在
            os.makedirs(dest_folder, exist_ok=True)
            logging.debug(f"目标文件夹: {dest_folder}")
            
            # 检查文件名是否包含特殊字符，如果有则替换
            safe_filename = "".join([c for c in filename if c.isalnum() or c in "._- "]).strip()
            logging.debug(f"安全文件名: {safe_filename}")
            
            if not safe_filename:
                safe_filename = f"image_{uuid.uuid4().hex[:8]}{file_ext}"
                logging.info(f"生成随机文件名: {safe_filename}")
            
            file_path = os.path.join(dest_folder, safe_filename)
            
            # 如果文件已存在，添加随机字符
            if os.path.exists(file_path):
                name, ext = os.path.splitext(safe_filename)
                safe_filename = f"{name}_{uuid.uuid4().hex[:6]}{ext}"
                file_path = os.path.join(dest_folder, safe_filename)
                logging.info(f"文件已存在，生成新文件名: {safe_filename}")
            
            # 读取并保存文件
            try:
                # 重置文件指针位置（如果之前已经读取过）
                await file.seek(0)
                content = await file.read()
                
                # 检查文件内容是否为空
                if not content:
                    logging.error("文件内容为空")
                    raise ValueError("文件内容为空")
                
                with open(file_path, "wb") as f:
                    f.write(content)
                
                logging.info(f"文件保存成功: {file_path}")
                return file_path
            except Exception as read_error:
                logging.error(f"读取或写入文件失败: {str(read_error)}")
                # 尝试直接通过文件对象保存
                try:
                    logging.info("尝试使用替代方法保存文件")
                    # 创建一个临时文件对象
                    with open(file_path, "wb") as buffer:
                        # 直接从文件对象复制内容
                        shutil.copyfileobj(file.file, buffer)
                    logging.info(f"使用替代方法保存文件成功: {file_path}")
                    return file_path
                except Exception as alt_error:
                    logging.error(f"替代保存方法也失败: {str(alt_error)}")
                    raise ValueError(f"无法保存文件，所有方法都失败: {str(alt_error)}")
        except Exception as e:
            logging.error(f"保存文件失败: {str(e)}")
            raise ValueError(f"保存文件 {getattr(file, 'filename', '未知')} 失败: {str(e)}")
    
    @staticmethod
    async def save_analysis_result(product_folder: str, result: str, image_texts: dict = None) -> str:
        """
        保存分析结果和图片文本内容
        
        Args:
            product_folder: 产品文件夹
            result: 分析结果
            image_texts: 图片OCR文本内容字典，键为图片名称，值为OCR文本
        
        Returns:
            结果文件路径
        """
        result_file = os.path.join(product_folder, "analysis_result.yaml")
        
        # 读取已有的数据（如果存在）
        existing_data = {}
        if os.path.exists(result_file):
            try:
                with open(result_file, "r", encoding="utf-8") as f:
                    existing_data = yaml.safe_load(f) or {}
            except Exception as e:
                print(f"读取现有分析结果出错: {str(e)}")
                existing_data = {}
        
        # 更新分析结果
        yaml_data = {
            "analysis_result": result,
            "image_texts": image_texts or existing_data.get("image_texts", {}),
            "updated_at": datetime.now().isoformat()
        }
        
        # 如果有新的图片文本内容，合并到已有数据中
        if image_texts and isinstance(image_texts, dict):
            existing_image_texts = existing_data.get("image_texts", {})
            if isinstance(existing_image_texts, dict):
                existing_image_texts.update(image_texts)
                yaml_data["image_texts"] = existing_image_texts
        
        # 将结果保存为YAML格式
        with open(result_file, "w", encoding="utf-8") as f:
            yaml.dump(yaml_data, f, allow_unicode=True, sort_keys=False, Dumper=yaml.SafeDumper)
        
        return result_file
    
    @staticmethod
    async def get_all_products() -> List[Dict[str, Any]]:
        """
        获取所有产品信息
        
        Returns:
            产品信息列表
        """
        products = []
        
        # 检查产品目录是否存在
        if not os.path.exists(settings.PRODUCTS_DIR):
            return products
        
        # 遍历所有产品文件夹
        for folder_name in os.listdir(settings.PRODUCTS_DIR):
            folder_path = os.path.join(settings.PRODUCTS_DIR, folder_name)
            if os.path.isdir(folder_path):
                # 获取产品名称(去掉UUID部分)
                product_name = folder_name.split("_")[0] if "_" in folder_name else folder_name
                
                # 检查是否有分析结果
                result_file = os.path.join(folder_path, "analysis_result.yaml")
                has_analysis = os.path.exists(result_file)
                
                # 获取图片数量
                image_count = sum(1 for f in os.listdir(folder_path) 
                                if os.path.isfile(os.path.join(folder_path, f)) 
                                and f.lower().endswith(('.png', '.jpg', '.jpeg')))
                
                products.append({
                    "id": folder_name,
                    "name": product_name,
                    "folder_path": folder_path,
                    "has_analysis": has_analysis,
                    "image_count": image_count,
                    "created_time": os.path.getctime(folder_path)
                })
        
        # 按创建时间排序
        products.sort(key=lambda x: x["created_time"], reverse=True)
        
        return products
    
    @staticmethod
    async def get_product_detail(product_id: str) -> Dict[str, Any]:
        """
        获取产品详情
        
        Args:
            product_id: 产品ID（文件夹名）
        
        Returns:
            产品详情
        """
        folder_path = os.path.join(settings.PRODUCTS_DIR, product_id)
        if not os.path.exists(folder_path):
            return {"error": "产品不存在"}
        
        # 获取产品名称
        product_name = product_id.split("_")[0] if "_" in product_id else product_id
        
        # 获取图片列表
        images = [f for f in os.listdir(folder_path) 
                if os.path.isfile(os.path.join(folder_path, f)) 
                and f.lower().endswith(('.png', '.jpg', '.jpeg'))]
        
        # 获取分析结果
        result_file = os.path.join(folder_path, "analysis_result.yaml")
        analysis_result = None
        if os.path.exists(result_file):
            with open(result_file, "r", encoding="utf-8") as f:
                analysis_data = yaml.safe_load(f)
                analysis_result = analysis_data.get("analysis_result")
        
        return {
            "id": product_id,
            "name": product_name,
            "folder_path": folder_path,
            "images": images,
            "analysis_result": analysis_result,
            "created_time": os.path.getctime(folder_path)
        }
    
    @staticmethod
    def get_product_image_texts(product_id: str) -> List[Dict[str, Union[str, float]]]:
        """
        获取产品图片OCR文本内容
        
        Args:
            product_id (str): 产品ID
            
        Returns:
            List[Dict[str, Union[str, float]]]: 包含图片相关信息和OCR文本的列表
        """
        try:
            product_folder = os.path.join(settings.PRODUCTS_DIR, product_id)
            result_file = os.path.join(product_folder, 'analysis_result.yaml')
            
            # 如果产品文件夹或结果文件不存在，返回空列表
            if not os.path.exists(product_folder) or not os.path.exists(result_file):
                return []
                
            # 读取分析结果文件
            with open(result_file, 'r', encoding='utf-8') as f:
                analysis_data = yaml.safe_load(f)
            
            # 获取图片文本数据
            image_texts = analysis_data.get('image_texts', {})
            if not image_texts:
                return []
                
            # 读取并添加其他图片元数据
            result = []
            
            # 处理image_texts为字典的情况
            if isinstance(image_texts, dict):
                for image_name, text_content in image_texts.items():
                    image_path = os.path.join(product_folder, image_name)
                    
                    # 检查图片是否存在
                    if os.path.exists(image_path):
                        # 获取图片大小
                        image_size = os.path.getsize(image_path)
                        formatted_size = FileManager.format_file_size(image_size)
                        
                        # 获取图片创建时间
                        creation_time = os.path.getctime(image_path)
                        
                        result.append({
                            'image_name': image_name,
                            'image_path': f'/static/uploads/products/{product_id}/{image_name}',
                            'text_content': text_content,
                            'confidence': 1.0,  # 默认置信度
                            'size': formatted_size,
                            'created_time': creation_time
                        })
            # 处理image_texts为列表的情况(兼容旧格式)
            elif isinstance(image_texts, list):
                for image_data in image_texts:
                    if not isinstance(image_data, dict):
                        continue
                        
                    image_filename = image_data.get('image_name', '')
                    image_path = os.path.join(product_folder, image_filename)
                    
                    # 检查图片是否存在
                    if os.path.exists(image_path):
                        # 获取图片大小
                        image_size = os.path.getsize(image_path)
                        formatted_size = FileManager.format_file_size(image_size)
                        
                        # 获取图片创建时间
                        creation_time = os.path.getctime(image_path)
                        
                        result.append({
                            'image_name': image_filename,
                            'image_path': f'/static/uploads/products/{product_id}/{image_filename}',
                            'text_content': image_data.get('text_content', ''),
                            'confidence': image_data.get('confidence', 0.0),
                            'size': formatted_size,
                            'created_time': creation_time
                        })
            
            return result
            
        except Exception as e:
            logging.error(f"Error getting product image texts: {str(e)}")
            return []
            
    @staticmethod
    async def check_missing_image_texts(product_id: str) -> list:
        """
        检查缺少OCR文本内容的图片
        
        Args:
            product_id: 产品ID
        
        Returns:
            缺少OCR文本内容的图片列表
        """
        product = await FileManager.get_product_detail(product_id)
        if "error" in product:
            return []
        
        # 获取已有的图片OCR文本内容
        image_texts = FileManager.get_product_image_texts(product_id)
        
        # 创建一个图像名称到文本内容的映射
        image_text_map = {item.get('image_name', ''): item.get('text_content', '') 
                         for item in image_texts if isinstance(item, dict)}
        
        # 查找缺少OCR文本内容的图片
        missing_images = []
        for image in product["images"]:
            if image not in image_text_map or not image_text_map[image]:
                missing_images.append(image)
        
        return missing_images

    @staticmethod
    async def get_product_files(product_id: str) -> List[Dict[str, Any]]:
        """获取产品的所有文件信息"""
        folder_path = os.path.join(settings.PRODUCTS_DIR, product_id)
        if not os.path.exists(folder_path):
            return []
            
        files = []
        for filename in os.listdir(folder_path):
            file_path = os.path.join(folder_path, filename)
            if os.path.isfile(file_path):
                file_ext = os.path.splitext(filename)[1].lower()
                files.append({
                    "id": filename,
                    "name": filename,
                    "type": file_ext[1:] if file_ext else "unknown",
                    "size": os.path.getsize(file_path),
                    "created_at": datetime.fromtimestamp(os.path.getctime(file_path)).isoformat()
                })
        return files
    
    @staticmethod
    async def get_file_content(product_id: str, file_id: str) -> Dict[str, Any]:
        """获取文件内容"""
        file_path = os.path.join(settings.PRODUCTS_DIR, product_id, file_id)
        if not os.path.exists(file_path):
            return None
            
        file_ext = os.path.splitext(file_id)[1].lower()
        
        # 根据文件类型返回内容
        if file_ext in settings.IMAGE_EXTENSIONS:
            return {
                "type": "image",
                "path": file_path
            }
        elif file_ext == '.pdf':
            return {
                "type": "pdf",
                "path": file_path
            }
        elif file_ext in ['.docx', '.doc']:
            return {
                "type": "docx",
                "path": file_path
            }
        elif file_ext in ['.xlsx', '.xls', '.csv']:
            return {
                "type": "excel",
                "path": file_path
            }
        elif file_ext == '.txt':
            with open(file_path, 'r', encoding='utf-8') as f:
                return {
                    "type": "txt",
                    "content": f.read()
                }
        elif file_ext in ['.json', '.yaml', '.yml']:
            with open(file_path, 'r', encoding='utf-8') as f:
                if file_ext == '.json':
                    content = json.load(f)
                else:
                    content = yaml.safe_load(f)
                return {
                    "type": file_ext[1:],
                    "content": content
                }
        else:
            return {
                "type": "unknown",
                "path": file_path
            }

    @staticmethod
    def format_file_size(size_in_bytes):
        """将文件大小从字节转换为人类可读格式"""
        if size_in_bytes < 1024:
            return f"{size_in_bytes} B"
        elif size_in_bytes < 1024 * 1024:
            return f"{size_in_bytes / 1024:.2f} KB"
        elif size_in_bytes < 1024 * 1024 * 1024:
            return f"{size_in_bytes / (1024 * 1024):.2f} MB"
        else:
            return f"{size_in_bytes / (1024 * 1024 * 1024):.2f} GB" 